<?php
/**
 * Clase para implementar el paradigma ActiveRecords con PHP
 *
 * Es un esfuerzo particual, donde busco generar una clase basandome en mi
 * propia logica. Obviamente ya existen muchas clases, solo que deseo hacerla por
 * mi propia cuenta a fin de ver que tal está mi intuición.
 * Esta clase deberá trabajar mancomunadamente con la clase Core, tambien de mi
 * creación.
 * Arpiro que la utilización de esta clase sea tan sencialla como las existentes
 * en el mercado.
 *
 * @author Jonas Reyes
 */
class ActiveRecords {
   /*
    * Atributos
    */
    protected $_propiedadesTabla=false;
    protected $_tablaNombre;
    protected $_tablaCampos;
    protected $_claseNombre;
    protected $_numCampos;
    protected $_numFilas;
    protected $_metaDataCampos;
    protected $_primaryKey;
    protected $_ultimoInsertId;


    function __construct() {
        if ($this->inicializaObjeto()) {
            return true;
        }else {
            return false;
        }
    }// Fin del Constructor

/**
 *Este método bueca unificar el arranque de los metodos importantes y necesarios para
 * la construcción inicial del Objeto Modelo.
 *
 * En escencia este metodo busca facilitar el uso de la clase y ahorrar código en el constructor,
 * se invocan los metodos ActiveRecords::setClaseNombre(), ActiveRecords::setTablaNombre(),
 * ActiveRecords::setTablaCampos(), ActiveRecords::setNumCampos(), ActiveRecords::setMetaDataCampos(),
 * ActiveRecords::setNumFilas() y ActiveRecords::setPropiedadesTabla().
 * 
 * @return boolean Verdadero o Falso segun se hayan arrancado o no todos los metodos asociados.
 */
    protected function inicializaObjeto() {
        /*
         * Todos los metodos deben ejecutarse bien, sino se retornará false.
         */
        if ($this->setClaseNombre() && $this->setTablaNombre() && $this->setTablaCampos() && $this->setNumCampos() && $this->setMetaDataCampos() && $this->setNumFilas() && $this->setPropidadesTabla()) {
            return true;
        }else {
            return false;
        }
        
    }// Fin del Metodo

/**
 *Obtiene el nombre de la tabla de bases de datos que se asociará al modelo y por consiguiente
 * al objeto heredado. Se basa en el nombre de la clase que hereda a ActiveRecods.
 * @return boolean Verdadero si se puede obtener el nombre de la tabla, de lo contrario
 * será falso.
 */
    public function setTablaNombre() {
        /*
         * obtenemos el nombre de la clase, y lo convertimos a minusculas.
         */
        if ($this->_tablaNombre = strtolower(get_class($this))) {
            return true;
        }else {
            return false;
        }
    }

/**
 *Este método devuelve el nombre de la tabla.
 * @return string representa el nombre de la tabla asociada al modelo. De lo contrario se retorna Un mensage de error y false.
 */
    public function getTablaNombre() {
        /*
         * se valida primeramente que primeró haya sido setteada la propiedad $_tablaNombre.
         */
        if (Core::ValidacionBasica($this->_tablaNombre)) {
            return $this->_tablaNombre;
        }else {
            Core::MsgError("Error, no se ha definido el nombre de la tabla: <b>getTablaNombre()</b>.");
            return false;
        }
    }//Fin del metodo

/**
 *Settea el nombre de la clase heredera de ActiveRecords
 * @return boolean Verdadero si se pudo obtener el nombre de la clase. de lo contrario se retorna false.
 */
    public function setClaseNombre() {
        if ($this->_claseNombre = get_class($this)) {
            return true;
        }else {
            return false;
        }
    }

/**
 *Devuelve el nombre de la Clase heredera de ActiveRecords
 * @return string retorna string si primeramente se ha setteado el nombre de la clase, sinó devolverá false.
 */
    public function getClaseNombre() {
        /*
         * se valido el setteo previo de la propiedad $_claseNombre
         */
        if (Core::ValidacionBasica($this->_claseNombre)) {
            return $this->_claseNombre;
        }else {
            Core::MsgError("Error, no se ha podido mostrar el nombre de la Clase: <b>getClaseNombre()</b>.");
            return false;
        }
    }

/**
 *Este metodo es escencial para el funcionamiejnto en general de la clase,
 * se encarga de construir un arreglo con los campos existentes en la tabla asociada
 * en la Base de datos.
 * @return boolean Devuelve Verdadero si se efectuó correctamente, de lo contrario devolverá false.
 */
    public function setTablaCampos() {
        /*
         * Iniciamos una conexión con la BD.
         */
        $bd = new BD2();
        $sql= "SHOW COLUMNS FROM {$this->_tablaNombre};";
        if ($bd->Query($sql)) {
            /*
             * si el resultado arroja cero filas, se debe interpretar que la tabla carece de campos aún.
             */
            if ($bd->NumFilas() < 1) {
                Core::MasError("Error, <b>::setTablaCampos()</b> ha determinado que su tabla carece de campos. Verifique sus datos.");
                return false;
            }else {
                // sino, devolvemos matriz con todos los campos
                $arreglo;
                while($campos = $bd->Fetch("a")){
                    $arreglo[]=array(
                        'campo'=>$campos['Field']
                        ,'tipo'=>$campos['Type']
                        ,'null'=>$campos['Null']
                        ,'key'=>$campos['Key']
                        ,'default'=>$campos['Default']
                    );
                }
                if (!empty ($arreglo)) {// Verificamos que la creacion del arreglo fue exitosa, si el arreglo tiene campos continuamos
                    $this->_tablaCampos=$arreglo;
                    //$bd->Free($bd->resultado); buscando errores asociados
                    //$bd->Close($bd->punteroBD); buscando errores asociados
                    // Asignacion de Clave Primaria
                    /*
                     * habiendo funcionado todo hasta aquí, podemos invocar el setteo de la clave primaria de la tabla.
                     */
                    $this->setPrimaryKey();
                    return true;
                }else {
                    Core::MsgError("Error, no se ha podido recoger los campos de la tabla '{$this->getTablaNombre()}'.");
                    //$bd->Free($bd->resultado);
                    //$bd->Close($bd->punteroBD);
                    return false;
                }
            }
        }
    }// Fin de Metodo

/**
 *Devuelve el arreglo que contiene los nombres de los campos de la tabla asociada.
 * @return array Devuelve un array si se efectuó correctamente, de lo contrario devolverá false.
 */
    public function getTablaCampos() {
        if (!empty ($this->_tablaCampos)) {
            return $this->_tablaCampos;
        }else {
            Core::MsgError("Error, <b>getTablaCampos()</b>: No se ha definido '\$_tablaCampos'.");
            return false;
        }
    }// fin de metodo

/**
 *Imprime una tabla HTML con los campos de la tabla asociada y sus características.
 * @return string si la ejecución fué exitosa devuelve una cadena de caracteres HTML.
 */
    public function displayTablaCampos() {
        $arreglo=$this->getTablaCampos();
        $display=''
        .'<table class="displayTablaCampos">'
        .'<tr>'
        .'<th>#</th><th>Campo</th><th>Tipo</th><th>Key</th><th>Null</th><th>Default</th>'
        .'</tr>';
$i=0;
/*
 * por cada registro en la matriz, devolveremos un campo y su contenido.
 */
foreach ($arreglo as $clave => $campo) {
      $display.=''
      .'<tr>'
      .'<td>'.++$i.'</td>'
      .'<td>'.$campo['campo'].'</td>'
      .'<td>'.$campo['tipo'].'</td>'
      .'<td>'.$campo['key'].'</td>'
      .'<td>'.$campo['null'].'</td>'
      .'<td>'.$campo['default'].'</td>'
      .'</tr>';
  }
  $display.='</table>';
  return $display;
    }//fin de metodo

/**
 *Este metodo asigna el numero de campos a la propiedad $_numCampos.
 * @return boolean si la ejecución fué correcta se devuelve verdadero, de lo contrario Falso.
 */
    public function setNumCampos() {
        /*
         * creamos una nueva conexión con la BD.
         */
        $bd = new BD2();
        $sql='SELECT * FROM '.$this->_tablaNombre.';';
        if ($bd->Query($sql)) {
            $this->_numCampos=$bd->NumColumnas();
            //$bd->Free($bd->resultado);
            //$bd->Close($bd->punteroBD);
            return true;
        }else {
            return false;
        }
    }// fin del metodo

/**
 *Retorna el número de campos que posee la tabla asociada al objeto-modelo.
 * @return integer Entero de ser correcta la ejecución del método, falso si es incorrecta, acompañado de un mensaje de error.
 */
    public function getNumCampos() {
        if (!empty ($this->_numCampos)) {
            return $this->_numCampos;
        }else {
            Core::MsgError("Error, <b>::getNumCampos()</b>: no se ha invocado aun el metodo '::setNumCampos().'");
            return false;
        }

    }

/**
 *Construye un arreglo con los metadatos de cada campo de la tabla asociada al objeto-modelo,
 * luego asigna dicho arreglo a la propiedad $_metaDataCampos.
 * @return booleam
 */
    public function setMetaDataCampos() {
         $bd = new BD2();

        if ($arreglo=$bd->FetchField($this->getTablaNombre())) {
            $this->_metaDataCampos=$arreglo;
            //$bd->Free($bd->resultado);
            //$bd->Close($bd->punteroBD);
            return true;
        }else {
            return false;
        }
    }// Fin del metodo

/**
 *Retorna un arreglo contentivo de los metadatos de cada campo del objeto-tabla asociado.
 * @return array de ser correcta la invocación retornará un arreglo, sino devolverá false.
 */
    public function getMetaDataCampos() {
        if (!empty ($this->_metaDataCampos)) {
            return $this->_metaDataCampos;
        }else {
            Core::MsgError("Error, <b>::getMetaDataCampos()</b>: no se ha invocado aun el metodo '::setMetaDataCampos().'");
            return false;
        }
    }

/**
 *Imprime una tabla HTML con la información contenioda en los metadatos de cada campo de la tabla asociada.
 * @return string retorna una cadena de carácteres HTML, si ocurrión algun error devielve false.
 */
    public function displayMetaDataCampos() {
             $arreglo=$this->getMetaDataCampos();
        $display=''
        .'<table class="displayMetaDataCampos">'
        .'<tr>'
        .'<th>#</th>'
        .'<th>Blob</th><th>Dimensión Máxima</th><th>Multiple Key</th><th>Nombre</th><th>Not Null</th>'
        .'<th>Numérico</th><th>Primary Key</th><th>Tabla</th><th>Tipo</th><th>Unique Key</th>'
        .'<th>Unsigned</th><th>Zerofill</th>'
        .'</tr>';
$i=0;
foreach ($arreglo as $clave => $campo) {
      $display.=''
      .'<tr>'
      .'<td>'.++$i.'</td>'
      .'<td>'.$campo['blob'].'</td>'
      .'<td>'.$campo['max_length'].'</td>'
      .'<td>'.$campo['multiple_key'].'</td>'
      .'<td>'.$campo['name'].'</td>'
      .'<td>'.$campo['not_null'].'</td>'
      .'<td>'.$campo['numeric'].'</td>'
      .'<td>'.$campo['primary_key'].'</td>'
      .'<td>'.$campo['table'].'</td>'
      .'<td>'.$campo['type'].'</td>'
      .'<td>'.$campo['unique_key'].'</td>'
      .'<td>'.$campo['unsigned'].'</td>'
      .'<td>'.$campo['zerofill'].'</td>'
      .'</tr>';
  }
  $display.='</table>';
  return $display;
    }

/**
 *Asigna el numero de filas o registros obtenidos en la última consulta hecha a la Base de datos.
 * @return boolean
 */
    public function setNumFilas() {
        $bd = new BD2();
        if ($this->_numFilas=$bd->NumFilas($this->getTablaNombre())) {
            return true;
        }else {
            return false;
        }
    }// Fin del metodo

/**
 *Devuelve el número de filas de la tabla asociada.
 * @return integer si la ejecución no es correcta devolverá falso y un mensaje de error.
 */
    public function getNumFilas() {
        if (!empty ($this->_numFilas)) {
            return $this->_numFilas;
        }else {
            Core::MsgError("Error, <b>::getNumFilas()</b>: no se ha invocado aun el metodo '::setNumFilas().'");
            return false;
        }

    }// Fin del Metodo

/**
 *Basandose en los campos de la tabla asociada, determina cual de ellos es Clave primaria
 * y lo asigna a la propiedad $_primaryKey.
 * @return boolean
 */
    public function setPrimaryKey() {
        /*
         * obtenemos el arreglo que contiene todos los campos de la tabla
         */
        $ArrayCampos=$this->getTablaCampos();
        /*
         * se chequean todos los registros hasta hayar aquél que esté declarado
         * como llave primaria.
         */
        if ($PrimaryKey=Core::arraySearchAssoc($ArrayCampos,'key','PRI')) {
            $this->_primaryKey=$PrimaryKey[0]['campo'];
            return true;
        }else {
            return false;
        }

    }//Fin del Metodo setPrimaryKEY()

/**
 *Devuelve el nombre del campo que es Clave o llave primaria de la tabla.
 * @return string retorna falso si aun no ha sido setteada la propiedad $_primaryKey.
 */
    public function getPrimaryKey() {
        if (!empty ($this->_primaryKey)) {
            return $this->_primaryKey;
        }else {
            Core::MsgError("Error, <b>getPrimaryKey()</b>: no se ha invocado el metodo setPrimaryKey().");
            return false;
        }
    }//Fin del Metodo

/**
 *Este metodo es usado para crear metrodos dinámicos, su funcionalidad en este momento es minima,
 * ya que solo posee por ahora dos metodos: ActiveRecods::buscarTodosPor_<NOMBRE_CAMPO> y
 * ActiveRecords::buscarPor_<NOMBRE_CAMPO>, el nombre del campo variará segun se desee al
 * momento de la busqueda, estos dos metodos devuelven una cadena de caracteres HTML
 * con una tabla HTML que muestra todos los valosres de todos los campos que coincidan
 * con el argumento pasado al método dinámico. El primero de los metodos solo devuelve
 * el primer registro que encuentre, el segundo devolverá una lista con todos los registros
 * coincidentes.
 * @param string $metodo
 * @param array $atributos
 * @return mixto puede devolver Datos enteros, Cadenas de texto HTML, valores Boobleanos, etc.
 */
    function __call($metodo,$atributos) {
        $tipo=gettype($atributos[0]);//Determinamos el tipo de dato del parametro para evitar errores en la consulta SQL
        if ($tipo=="string") {// si la variable es string concatenamos su valor entre comillas simples para evitar errores en MySQL.
            $valor="'".$atributos[0]."'";//si indica el indice 0 en el arreglo $atributos[] porque sabemos que los metodos procesados
            //hasta ahora solo poseen un solo argumento
        }else {
            $valor=$atributos[0];
        }

        // empezando la acción
        $metodoPartido = explode('_', $metodo); // separamos la cadena usada para invocar al metodo,
        $raizMetodo=$metodoPartido[0];          //ya que de esta forma sabremos el nombre del campo
        $campoMetodo=$metodoPartido[1];         // en el que debemos buscar.
        //-- Instanciacion de conexión a la BD.
        $bd = new BD2();
        $bd->setCharset();
        

        if ($raizMetodo == "buscarPor") {// si el metodo empezaba con "buscarPor" sabemos que quiere solo el primer registro
            $sql="SELECT * FROM {$this->_tablaNombre} WHERE {$campoMetodo} = {$valor} LIMIT 0,1;";
            $bd->Query($sql);
            $busqueda = $bd->Fetch();
            $afectados = $bd->Afectados();
            if ($afectados < 1) {echo "<hr />No se encontro <b>{$valor}</b> en {$campoMetodo}.<hr />"; return false;}
            echo("<hr />Afechados: $afectados<hr />");
            echo("<br><b>$sql</b><br>");

            //Construcción de la tabla de salida de datos
            $tabla='<table class=\"buscarTodosPor\">';
            $tablaCabecera='<tr>';
            $tablaCuerpo='<tr>';
            //llenando Cabecera y Cuerpo de tabla
            foreach ($this->_tablaCampos as $variable => $contenido) {
                $tablaCabecera.='<th>'.$contenido['campo'].'</th>';
                $tablaCuerpo.='<td>'.$busqueda[$contenido['campo']].'</td>';
            }
            $tablaCabecera.='</tr>';
            $tablaCuerpo.='</tr>';

            //Unificando datos de tabla
            $tabla.=$tablaCabecera.$tablaCuerpo.'</table>';

            //Devolviendo tabla
            return $tabla;

        }elseif ($raizMetodo=="buscarTodosPor") {   // si espezabe el metodo por "buscarTodosPor"
                                                    // sabremos que se debe construir una lista con todos
                                                    // los registros coincidentes.
            $sql="SELECT * FROM {$this->_tablaNombre} WHERE {$campoMetodo} = {$valor};";
            $bd->Query($sql);
            $busqueda=null;

            $afectados = $bd->NumFilas();
            if ($afectados < 1) {echo "<hr />No se encontro <b>{$valor}</b> en {$campoMetodo}.<hr />"; return false;}
            #
            #echo("<hr />Afechados: $afectados<hr />");
            #echo("<br><b>$sql</b><br>");
            #
            #
            //Construcción de la tabla de salida de datos
            $tabla='<table class=\"buscarTodosPor\">';
            $tablaCabecera='<tr>';
            
            //llenando Cabecera de tabla
            foreach ($this->_tablaCampos as $variable => $contenido) {
                $tablaCabecera.='<th>'.$contenido['campo'].'</th>';
            }
            $tablaCabecera.='</tr>';

            $tablaCuerpo="";
            
            //llenando Cuerpo de tabla
            while($busquedaArray = $bd->Fetch()){// Logica parida, uff, como me costo desarrollarla con mucho sueño
                $tablaCuerpo.='<tr>';
                foreach ($this->_tablaCampos as $variable => $contenido) {
                $tablaCuerpo.='<td>'.$busquedaArray[$contenido['campo']].'</td>';
            }// fin foreach

            $tablaCuerpo.='</tr>';

            }//fin de while
         
            //Unificando datos de tabla
            $tabla.=$tablaCabecera.$tablaCuerpo.'</table>';

            //Devolviendo tabla
            return $tabla;
        }
// - Liberamos consulta realizada en BD.
        //$bd->Free($bd->resultado);

//Fin del Estructura elseif que condiciona el metodo a crear.

    }//Fin del Metodo

/**
 *Permite crear un nuevo registro en la tabla asociada.
 * @param array $datos Arreglo asociativo con los nombre de los campos y valores.
 * @return boolean
 */
    public function crear($datos = null) {
        if (!$datos) {// si no se pasa argumentos
            Core::MsgError("Error, para invocar el método <b>guardar()</b> debe ingresar un array válido como parametro.");
            return false;
        }
        if (!is_array($datos)) {//si el argumento no es un arreglo
            Core::MsgError("Error, para invocar el Metodo <b>guardar()</b> debe contener un array como parametro.");
            return false;
        }

$propiedadesVars="";
$propiedadesCamp="";
$nomCampos="";
$valCampos="";
foreach ($datos as $key => $valor) {

    $tipo=gettype($valor);//Determinamos el tipo de dato del parametro para evitar errores en la consulta SQL
    if ($key=="password") { // si uno de los campos tiene el nombre de 'password'
                            //se encripta su contenido. esto evolucionará hasta hacer
                            //variable el algoritmo de encriptamiento
        $valCampos.="MD5('".$valor."'),";
    }else{
        if ($tipo=="string") {  // cualquier otro campo que sea strin, será escapado
                                //con comillas simples para evitar errores de MySQL
            $valCampos.="'".$valor."',";
        }else{
            $valCampos.=$valor.",";// si no es Strin se asume que es double o integer, por lo cual no se requiere escapar para MySQL
        }
    }

        $nomCampos.=$key.",";// El nombre de los campos se pasa uno a uno separandose entre sí por una coma ','
}
/*
 * A continuacion verificamos si la tabla posee campos creado y modificado.
 */
foreach ($this->_propiedadesTabla as $keys => $vars) {
        if ($keys=="creado") {// si los posee prosedemos a asignar su valos automáticamente
            $propiedadesVars.="'".Core::GetDateTime("total")."',";
            $propiedadesCamp.="creado,";
        }elseif ($keys=="modificado") {
            $propiedadesVars.="'".Core::GetDateTime("total")."',";
            $propiedadesCamp.="modificado,";
        }
}// fin del foreach
$nomCampos.=$propiedadesCamp;
$valCampos.=$propiedadesVars;
$nomCampos=substr($nomCampos, 0, -1);// Eliminamos la ultima coma en el contenido de la variable
$valCampos=substr($valCampos, 0, -1);
$sql="INSERT INTO {$this->_tablaNombre}($nomCampos) values($valCampos);";
$bd = new BD2();
$bd->Query($sql);
$afectados = $bd->Afectados();
if ($afectados == 1) {
    $this->_ultimoInsertId=$bd->getInsertId();  
    return true;
}else {

    return false;
}


    }//Fin del metodo

/**
 *Devuelve el ultimo ID generado en la inserción de registros mas reciente en la BD.
 * @return integer devuelve false si no se ha setteado antes el ID
 */
    public function getUltimoInsertId() {
        if (!empty ($this->_ultimoInsertId)) {
            return $this->_ultimoInsertId;
        }else {
            return false;
        }
    }// Fin del metodo

/**
 *Settea la propiedad '$_propiedadesTabla', construyendolo como array. estas propiedades
 * son las que asignarán o no valores a los registros de la tabla asociada.
 * @return boolean
 */
    public function  setPropidadesTabla() {
        if ($campos=$this->getTablaCampos()) {
            foreach ($campos as $campo) {
            $this->_propiedadesTabla[$campo['campo']]=false;
        }// fin Foreach
        return true;
        }
        return false; // Si se llega esta linea es porque se dio un error.
    }// Fin del Metodo.

/**
 *Devuelve un array con el nombre de los campos de la tabla almacenados en $_propiedadesTabla.
 * @return array Si ocurre un problema devolverá false.
 */
    public function getPropiedadesTabla() {
        if (!$this->_propiedadesTabla) {
            Core::MsgError("Error, para usar getPropiedadesTabla() debe settearse antes las propiedades de la tabla.");
            return false;
        }
        $arregloTemp = array();
        foreach ($this->_propiedadesTabla as $key => $val) {
            $arregloTemp[]=$key;
        }// fin del foreach

        return $arregloTemp;
    }// Fin del Metodo

/**
 *Setead de forma dinámica las propiedades de la tabla, es el unico metodo que permitirá
 * trabajar con la asignación y recuperacion de los datos de cada registro.
 * @param string $propiedad
 * @param mixto $valor Este valor a asignar a la propiedad debe coincidir con
 * el tipo de dato declarado en la construcción de la tabla. Además no podrá
 * exceder los limites en ella establecidos.
 * @return boolean
 */
    public function  __set($propiedad,  $valor=null) {
        $tipoDato=gettype($valor);//Almacenamos el tipo de dato de la Variable
        $valor=trim($valor);//Eliminamos espacios en los extremos del contenido
        //de la variable, pero esta accion transforma en string el contenido.
        //por eso a continuación devolveremos el tipo original al valor pasado.
        if ($tipoDato=="integer") {$valor=(integer)$valor;}
        elseif ($tipoDato=="string") {$valor=(string)$valor;}
        elseif ($tipoDato=="double") {$valor=(double)$valor;}
        elseif ($tipoDato=="boolean") {$valor=(boolean)$valor;}

        
        $cuenta=0;// Contador para determinar si la propiedad existe o no.
$prop=$this->getPropiedadesTabla();
foreach ($prop as $num => $var) {
    if ($var != $propiedad) {
        $cuenta++;
    }
}//fin del foreach

if ($cuenta == $this->getNumCampos()) {
    Core::MsgError("Error, no existe la propiedad '$propiedad'.");
    return false;
}
if (!$valor) {
    Core::MsgError("Error, no puede definir '$propiedad' como vacio.");
    return false;
}
//-- al llegar a este punto se puede realmente settear la propiedad ya
//que cuenta se ha mantenido en cero, de lo contrario significaria que la propiedad
//que se intenta settear no existe dentro del array propiedadesTabla.

if ($this->_propiedadesTabla[$propiedad]=$valor) {
    return true;
}else {
    return false;
}


    }// Fin del metodo

/**
 *Devuelve de forma dinamica el contenido de las propiedades de cada registro previamente
 * seleccionado, no se accederá a ningun otro dato. es la forma única de poder recuperar dicha información.
 * @param string $propiedad nombre del campo del registro cuyo valor se desea obtener.
 * @return mixto el valor devuelto dependerá del tipo de dato de cada campo de la tabla.
 */
    public function  __get($propiedad) {
        $prop=$this->getPropiedadesTabla();//Obtenemos un array con los atributos especificos de la tabla
        foreach ($prop as $num => $var) {
            if ($var != $propiedad) {
                $cuenta++;
            }else {// Si la propiedad existe devolvemos su contenido.
                return $this->_propiedadesTabla[$propiedad];
            }
        }//fin del foreach

        if ($cuenta == $this->getNumCampos()) {
            Core::MsgError("Error, no existe la propiedad '$propiedad'.");
            return false;
        }

    }//Fin del método

/**
 *
 * @param array $datos se podrá pasar un arreglo con todoslos campos del
 * registro a modificar, incluyendo el ID, sin embargo esta posibilidad aun está
 * desactivada hasta conseguir una forma eficiente de procesar la información.
 * Sin embargo, este metodo trabaja perfectamente si se invoca luego de haber
 * invocado ActiveRecords::selectId() pasando como argumento el ID del registro a modificar
 * y posteriormente seteando sus datos a traves de sus propiedades. Proximamente se anexará un ejemplo.
 * @return boolean ademas se retornará un mensaje de exito o error segun se de el caso.
 */
        public function guardar($datos = null) {
            
        /* nueva logica para guardar:
         * si este metodo podra funcionar de dos modos:
         * 1. Cuando se le pase un Array de Datos, se debe incluir el ID del elemento
         * existente a guardar. Se verificara la existencia del ID. (no se implementará por ahora)
         * 2. Cuando no se le pase argumentos, se verificará que hayan sido 
         * setteadas las propiedades por completo y se tomará el ID contenido en ellas.
         * Más adelante se implementará lectura de campos null, y estos no será de
         * declaración obligatoria. si el registro posee campos de nombre 'creado'
         * con una asignación null se ingresará en la BD por defecto '0000-00-00',
         * si la tabla posee campo con el nombre de 'modificado', a cada registro
         * actualizado se le definirá automaticamente la fecha y hora de ese evento.
         * Si la tabla posee un campo denominado 'password' se encriptará automáticamente
         * en MD5 su contenido.
         */
            $bd = new BD2();// Conectamos con la BD

        if (!$datos) {
            //Buscamos que las propiedades hayan sido setteadas
            if ($this->_propiedadesTabla['id']!=null) {
                    $id=$this->_propiedadesTabla['id'];
                }
                
            $propiedades="";
            foreach ($this->_propiedadesTabla as $keys => $vars) {
                
                if ($keys=="id") {
                    $propiedades.="";
                }else {
                    $tipo=gettype($vars);
                    
                        if (($keys=="creado") && ($vars==false)) {
                            $propiedades.="";
                        }elseif ($keys=="modificado") {
                            $propiedades.=$keys."='".Core::GetDateTime('total')."',";
                        }elseif ($keys=="password") {
                            // iniciamos consulta en BD para verificar si se ha modificado o no el password.
                            $sqlAux="SELECT password, MD5('{$this->_propiedadesTabla['password']}') password_nuevo FROM {$this->getTablaNombre()} WHERE id={$this->_propiedadesTabla['id']};";
                            $largoP1=strlen($this->_propiedadesTabla['password']);
                            $bd->Query($sqlAux);
                            $numAfect=$bd->Afectados();
                            if ($numAfect==1) {
                                $pass=$bd->Fetch();
                                if (($pass['password'] == $this->_propiedadesTabla['password']) || ($pass['password']==$pass['password_nuevo'])) {//No se modifica el password si las cadenas son iguales
                                    $propiedades.="";//en este contexto no es desabilitados, solo no hay necesidad de reescribir el mismo password en la tabla
                                }else {// Al noser iguales las cadenas, generamos un nuevo password.
                                    $propiedades.=$keys."=MD5('".$this->_propiedadesTabla[$keys]."'),";
                                }
                                
                            }else {
                                Core::MsgError("Error, no se pudo concluir consulta de validación del password en la tabla {$this->getTablaNombre()}.");
                                $propiedades.="";//deshabilitado temporalmente
                            }
                        }else{
                            if ($tipo=="string") {
                                $propiedades.=$keys."='".$this->_propiedadesTabla[$keys]."',";
                            }else{
                                $propiedades.=$keys."=".$this->_propiedadesTabla[$keys].",";
                            }         
                        }                   
                }//fin del else principal
            }// fin del foreach
            $propiedades=substr($propiedades, 0, -1);
            $sql="UPDATE {$this->_tablaNombre} SET {$propiedades} WHERE id={$id};";

            $bd->Query($sql);
            $afectados = $bd->Afectados();
            if ($afectados == 1) {
                Core::MsgExito("Se ha modificado exitosamente el registro <strong>'$id'</strong> en <strong>'{$this->getTablaNombre()}'</strong>.");
                if ($this->selectId($id)) {
                    @$bd->Free($bd->resultado);
                    $bd->Close($bd->punteroBD);

                    return true;
                }else {
                    Core::Msg("Los datos han sido modificados pero no se pudo Settear las propiedades del ultimo objeto.");
                    return true;
                }
                
            }else {
                Core::MsgError("Error, no se pudo modificar el registro <strong>'$id'</strong> en <strong>'{$this->getTablaNombre()}'</strong>.");
                return false;
            }
        }
    }//Fin del metodo

/**
 *Este método selecciona un registro del modelo cuyo ID corresponde con el pasado como argumento.
 *
 * Luego de usar este metodo el objeto pasa a tener definidas sus propiedades con los
 * valores hayados en la consulta a la BD. Finalmente devuelve un array si la
 * operacion ha sido correcta o false si no lo es.
 * @param integer $id representa el id o clave primaria del registro sobre el que
 * deseamos obtener los datos para settear las propiedades del objeto actual del modelo.
 * @return array Cuando se ejecuta correctamente el metodo devuelve un array
 * asociativo con el nombre de los campos y datos del registro correspondiente
 * al id pasado como parametro.
 */
    public function selectId($id=null) {
        $id=trim($id);
        if (!$id) {
            Core::MsgError("Error, debe ingresar un ID para usar el metodo '::selectId()'");
            return false;
        }
        $bd = new BD2();
       
            $sql="SELECT * FROM {$this->getTablaNombre()} WHERE id=$id LIMIT 0,1;";
            $bd->Query($sql);
            $busqueda = $bd->Fetch();
            $afectados = $bd->Afectados();
            if ($afectados < 1) {echo "<hr />No se encontro un registro en '{$this->getTablaNombre()}' con el ID <b>$id</b>."; return false;}

            //Setteo de Propiedades del Objeto
            foreach ($busqueda as $variable => $contenido) {
                $this->_propiedadesTabla[$variable]=$contenido;
            }

            return $this->_propiedadesTabla;

        
    }//fin del metodo

/**
 *Este método elimina el registro cuyo ID coincida con el argumento pasado.
 * @param integer $id
 * @return boolean tambien imprime un mensaje de exito o fracaso de la operación.
 */
public function eliminar($id=null) {
    $id=(integer)trim($id); // Eliminamos espacios
    
    if ($id) {// si el argumento existe
        if (is_int($id)) { // si el argumento es entero, es decir, si realmente es un ID.
            //Ahora solo queda verificar que existe el id
            if ($this->existe($id)) {
                $bd = new BD2();
                $sql="DELETE FROM {$this->getTablaNombre()} WHERE id=$id;";
                $bd->Query($sql);
                $busqueda = $bd->Fetch("a");
                $afectados = $bd->Afectados();
                if ($afectados==1) {
                    Core::MsgExito("Se ha eliminado el registro '<strong>$id</strong>' de '<strong>{$this->getTablaNombre()}</strong>'.");
                    return true;
                }else {
                    Core::MsgError("Error, no se pudo eliminar el registro '<strong>$id</strong>' en la tabla '<strong>{$this->getTablaNombre()}</strong>'.");
                    return false;
                }
                }else {
                    Core::MsgError("Error, no existe el registro '<strong>$id</strong>' en la tabla '<strong>{$this->getTablaNombre()}</strong>'.");
                    return flase;
                }
        }else {
            Core::MsgError("Error, el argumento a pasar al metodo ::<strong>eliminar()</strong> debe ser un número entero.");
            return false;
        }
        
    }
}// Fin del metodo

/**
 *Verifica la existencia o no de un registro en la tabla actual que coincida con el ID
 * pasado como argumento.
 * @param integer $id hace referencia al ID cuya existencia se desea validar
 * @return boolean verdadero si el registro existe, falso en caso contrario.
 */
public function existe($id=null) {
    //$id=(integer)trim($id);
    if (!$id) {
        Core::MsgError("Error al invocar método ::<strong>existe()</strong> sin argumento valido.");
        return false;
    }else {
        if (is_int($id)) {
            $bd = new BD2();
            $sql="SELECT * FROM {$this->getTablaNombre()} WHERE id=$id;";
            $resp=$bd->Query($sql);//mysql_query($sql, $bd->punteroBD);
            $busqueda = $bd->Fetch();
            $afectados = $bd->NumFilas();
            if ($afectados==1) {
                return true;//el registro existe
            }else {
                return false;//no existe el registro
            }

        }else {
            Core::MsgError("Error, el argumento a pasar al metodo ::<strong>existe()</strong> debe ser un número entero.");
            return false;
        }
    }
}//Fin del metodo

public function getTodos($where='DEFAULT',$order='DEFAULT') {

    $where=trim($where); // evitamos que hayan espacios adicionales
    $order=trim($order);

    if(!$where){
         Core::MsgError("ERROR, método: (<strong>ActivedRecords::getTodos()</strong>) la instrucción WHERE no es válida.");
        return false;
    }elseif(!$order){
         Core::MsgError("ERROR, método: (<strong>ActivedRecords::getTodos()</strong>) la instrucción ORDER BY no es válida.");
        return false;
    }

    if ($where=='DEFAULT') {
        $where="";
    }else {
        $where=" ".$where;// separamos la instrucion con un espacio
    }

    if ($order=='DEFAULT') {
        $order=" ORDER BY id";// por convencion, toda tabla debe tener por llave primaria el campo id.
    }else {
        $order=" ".$order;
    }

    $sql="SELECT * FROM {$this->getTablaNombre()}{$where}{$order};";
    $bd = new BD2();
    $bd->Query($sql);
    $numFilas=$bd->NumFilas();
    if ($numFilas < 1) {
        return false;
    }else {
        $arregloFilas= array();//creamos el arreglo multidimencional que guardara el resultado

        while($fila=$bd->Fetch()){
            $arregloFilas[]=$fila;
        }

        return $arregloFilas;

    }//Fin  del ELSE

}//Fin del metodo
    
    }// Fin de clase
?>